#pragma once
#include <iostream>
#include "../mqcommon/Helper.hpp"
#include "../mqcommon/logger.hpp"
#include "../mqcommon/mq_msg.pb.h"
#include <mutex>
#include <memory>
namespace mq_zxws
{
    struct Binding{
        using ptr = std::shared_ptr<Binding>;
        std::string _exchange_name;
        std::string _msgqueue_name;
        std::string _binding_key;
        Binding(){}
        Binding(const std::string& exchange_name, const std::string & msgqueue_name, const std::string & binding_key)
            :_exchange_name(exchange_name),_msgqueue_name(msgqueue_name), _binding_key(binding_key)
        {}
    };
    using MsgQueueBindingMap = std::unordered_map<std::string, Binding::ptr>; // 记录匹配规则和队列关系
    using BindingMap = std::unordered_map<std::string, MsgQueueBindingMap>; // 记录交换机和队列关系
    // 队列和绑定信息是一一对应的，交换机和队列是一对多的关系
    // 先定义一个队列名和绑定信息的映射 MsgQueueBindingMap
    // 定义一个交换机名称和队列绑定信息的映射关系 BindingMap
    class BindingMapper{
    public:
        BindingMapper(const std::string & dbfile):_sql_helper(dbfile){
            std::string parent = FileHelper::parentDirectory(dbfile);
            FileHelper::createDirectory(parent);
            assert(_sql_helper.open());
            createTable();
        }
        void createTable(){
            std::string sql = "create table if not exists binding_table(\
                exchange_name varcahr(32), \
                msgqueue_name varcahr(32), \
                binding_key varchar(128));";
            if(!_sql_helper.exec(sql, nullptr, nullptr)){
                DLOG("创建binding表失败");
                abort();
            }
        }
        void removeTable(){
            std::string sql = "drop table if exists binding_table;";
            if(!_sql_helper.exec(sql, nullptr, nullptr)){
                DLOG("删除binding表失败");
                abort();
            }
        }
        bool insert(Binding::ptr &binding){
            char sql[4096]{};
            sprintf(sql, "insert into binding_table values('%s', '%s', '%s');", 
                    (binding->_exchange_name).c_str(), (binding->_msgqueue_name).c_str(), (binding->_binding_key).c_str());
            return _sql_helper.exec(sql, nullptr, nullptr);
        }
        void remove(const std::string & ename, const std::string& qname){
            std::string sql = "delete from binding_table where exchange_name = '" + ename + "' and msgqueue_name = '" + qname + "';";
            _sql_helper.exec(sql, nullptr, nullptr);
        }
        void removeExchangeBindings(const std::string& ename){
            std::string sql = "delete from binding_table where exchange_name = '" + ename + "';";
            _sql_helper.exec(sql, nullptr, nullptr);
        }
        void removeMsgqueueBindings(const std::string& qname){
            std::string sql = "delete from binding_table where msgqueue_name = '" + qname + "';";
            _sql_helper.exec(sql, nullptr, nullptr);
        }
        BindingMap recovery(){
            BindingMap result;
            std::string sql = "select * from binding_table;";
            _sql_helper.exec(sql, SelectCallback, &result);
            return result;
        }
    private:
        static int SelectCallback(void *args, int col, char ** row, char ** col_name){
            using ExchangeMap = std::unordered_map<std::string, Binding::ptr>;
            BindingMap* res = static_cast<BindingMap*>(args);
            auto bdp = std::make_shared<Binding>(row[0], row[1], row[2]);
            auto& tmp = (*res)[bdp->_exchange_name];
            tmp.insert(std::make_pair(bdp->_msgqueue_name, bdp));
            return 0;
        }
        SqliteHelper _sql_helper;  
    };
    class BindingManager{
    public:
        using ptr = std::shared_ptr<BindingManager>;
        BindingManager(const std::string & dbfile):_mapper(dbfile){
            _bindings = _mapper.recovery();
        }
        bool Bind(const std::string& ename, const std::string & qname, const std::string & key, bool durable){
            std::lock_guard<std::mutex> lock(_mutex);
            auto it = _bindings.find(ename);
            if(it != _bindings.end() && it->second.find(qname) != it->second.end())
                return true;
            Binding::ptr bdp = std::make_shared<Binding>(ename, qname, key);
            if(durable){
                if(!_mapper.insert(bdp))
                    return false;
            }
            _bindings[ename][qname] = bdp;
            return true;
        }
        void unBind(const std::string& ename, const std::string & qname){
            std::lock_guard<std::mutex> lock(_mutex);
            auto eit = _bindings.find(ename);
            if(eit == _bindings.end()) return;
            auto qit = eit->second.find(qname);
            if(qit == eit->second.end()) return;
            _mapper.remove(ename, qname);
            eit->second.erase(qit);
        }
        void removeExchangeBindings(const std::string& ename){
            auto it = _bindings.find(ename);
            if(it == _bindings.end()) return;
            _mapper.removeExchangeBindings(ename);
            _bindings.erase(it);
        }
        void removeMsgqueueBindings(const std::string& qname){
            std::lock_guard<std::mutex> lock(_mutex);
            _mapper.removeMsgqueueBindings(qname);
            for(auto it = _bindings.begin(); it != _bindings.end(); ++it){
                it->second.erase(qname);
            }
        }
        MsgQueueBindingMap getExchangeBindings(const std::string& ename){
            std::lock_guard<std::mutex> lock(_mutex);
            auto it = _bindings.find(ename);
            if(it == _bindings.end()) return MsgQueueBindingMap();
            return it->second;
        }
        Binding::ptr getBinding(const std::string& ename, const std::string& qname){
            std::lock_guard<std::mutex> lock(_mutex);
            auto eit = _bindings.find(ename);
            if(eit == _bindings.end()) return Binding::ptr();
            auto qit = eit->second.find(qname);
            if(qit == eit->second.end()) return Binding::ptr();
            return qit->second;
        }
        bool exists(const std::string& ename, const std::string& qname){
            std::lock_guard<std::mutex> lock(_mutex);
            auto eit = _bindings.find(ename);
            if(eit == _bindings.end()) return false;
            auto qit = eit->second.find(qname);
            if(qit == eit->second.end()) return false;
            return true;
        }
        size_t size(){
            std::lock_guard<std::mutex> lock(_mutex);
            size_t total = 0;
            for(auto it = _bindings.begin(); it != _bindings.end(); ++it){
                total += it->second.size();
            }
            return total;
        }
        void clear(){
            std::lock_guard<std::mutex> lock(_mutex);
            _mapper.removeTable();
            _bindings.clear();
        }
    private:
        std::mutex _mutex;
        BindingMapper _mapper;
        BindingMap _bindings;
    };
}